//
//  HyperlinkLabel.swift
//  GreenDeal
//
//  Created by Moennig Q on 2023/5/29.
//

import UIKit

final class HyperlinkLabel: UILabel {
    
    // MARK: Creating the Label
    
    private var allAttributedText: NSMutableAttributedString = NSMutableAttributedString()
    private var allUrlAndTitle:[String:String] = [String:String]()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        numberOfLines = 0
        isUserInteractionEnabled = true
    }
    
    override var attributedText: NSAttributedString? {
        get {
            return super.attributedText
        }
        set {
            super.attributedText = {
                guard let newValue = newValue else { return nil }
                // Apply custom hyperlink attributes
                let text = NSMutableAttributedString(attributedString: newValue)
                text.enumerateAttribute(.hyperlink, in: NSRange(location: 0, length: text.length), options: .longestEffectiveRangeNotRequired) { (value, subrange, _) in
                    guard let value = value else { return }
                    assert(value is URL)
                    text.addAttributes(hyperlinkAttributes, range: subrange)
                }
                // Fill in font attributes when not set
                text.enumerateAttribute(.font, in: NSRange(location: 0, length: text.length), options: .longestEffectiveRangeNotRequired) { (value, subrange, _) in
                    guard value == nil, let font = font else { return }
                    text.addAttribute(.font, value: font, range: subrange)
                }
                return text
            }()
        }
    }
    
    // MARK: Finding Hyperlink Under Touch
    var hyperlinkAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor(hexStr: Constant.ThemeColorHexStr)]
    
    var hyperTextAlignment:NSTextAlignment = .left
    
    var hyperCustomStyling:Bool = false
    
    var didTapOnURL: (String,URL) -> Void = {(title,url) in
        if UIApplication.shared.canOpenURL(url) {
            UIApplication.shared.open(url, options: [:], completionHandler: { success in
                if success {
                    print("Opened URL \(url) successfully")
                }
                else {
                    print("Failed to open URL \(url)")
                }
            })
        }
        else {
            print("Can't open the URL: \(url)")
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let url = self.url(at: touches) {
            if let linkUrl = url.1 {
                didTapOnURL(url.0,linkUrl)
            }
        }
        else {
            super.touchesEnded(touches, with: event)
        }
    }
    
    private func url(at touches: Set<UITouch>) -> (String , URL?)? {
        guard let attributedText = attributedText, attributedText.length > 0 else { return nil }
        guard let touchLocation = touches.sorted(by: { $0.timestamp < $1.timestamp } ).last?.location(in: self) else { return nil }
        guard let textStorage = preparedTextStorage() else { return nil }
        let layoutManager = textStorage.layoutManagers[0]
        let textContainer = layoutManager.textContainers[0]
        
        let characterIndex = layoutManager.characterIndex(for: touchLocation, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        guard characterIndex >= 0, characterIndex != NSNotFound else { return nil }

        // Glyph index is the closest to the touch, therefore also validate if we actually tapped on the glyph rect
        let glyphRange = layoutManager.glyphRange(forCharacterRange: NSRange(location: characterIndex, length: 1), actualCharacterRange: nil)
        let characterRect = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
        guard characterRect.contains(touchLocation) else { return nil }
        
        // Link styled by Apple
        if let url = textStorage.attribute(.link, at: characterIndex, effectiveRange: nil) as? URL {
            return (self.allUrlAndTitle[url.absoluteString] ?? "" ,url)
        }
        
        // Custom link style
        let url = textStorage.attribute(.hyperlink, at: characterIndex, effectiveRange: nil) as? URL
        return (self.allUrlAndTitle[url?.absoluteString ?? ""] ?? ""  ,url)
    }
    
    private func preparedTextStorage() -> NSTextStorage? {
        guard let attributedText = attributedText, attributedText.length > 0 else { return nil }
        
        // Creates and configures a text storage which matches with the UILabel's configuration.
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: bounds.size)
        textContainer.lineFragmentPadding = 0
        let textStorage = NSTextStorage(string: "")
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)
        
        textContainer.lineBreakMode = lineBreakMode
        textContainer.size = textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines).size
        textStorage.setAttributedString(attributedText)
        
        return textStorage
    }
}

extension NSAttributedString.Key {
    static let hyperlink = NSAttributedString.Key("hyperlink")
}

extension HyperlinkLabel {
    
    static func banner(content: String, alignment: NSTextAlignment, customStyling: Bool, tapHandler: @escaping (String,URL) -> Void) -> HyperlinkLabel {
        
        let attributedString = NSMutableAttributedString(string: content)
        let attrNo: [NSAttributedString.Key : Any] = [.font: R.font.interMedium(size: 14 * Constant.currentSizeScale)!,.foregroundColor: UIColor(hexStr: "#292D32")]
        attributedString.addAttributes(attrNo, range: NSRange(location: 0, length: attributedString.length))
        
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = alignment
        attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
        
        let label = HyperlinkLabel()
        label.allAttributedText = attributedString;
        label.attributedText = attributedString
        label.hyperTextAlignment = alignment
        label.hyperCustomStyling = customStyling
        label.translatesAutoresizingMaskIntoConstraints = false
        label.didTapOnURL = tapHandler
        return label
    }
    
    func addStr(content: String)
    {
        let attributedStringOld = self.allAttributedText
        
        let attributedStringNew = NSMutableAttributedString(string: content)
        let attrNo: [NSAttributedString.Key : Any] = [
            .font: R.font.interMedium(size: 14 * Constant.currentSizeScale)!,
            .foregroundColor: UIColor(hexStr: "#292D32")
        ]
        attributedStringNew.addAttributes(attrNo, range: NSRange(location: 0, length: attributedStringNew.length))
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = self.hyperTextAlignment
        attributedStringNew.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedStringNew.length))

        attributedStringOld.append(attributedStringNew)
        self.attributedText = attributedStringOld
    }
    
    
    func addBanner(linkContentStr: String, linkStr: String, customStyling: Bool) {
        if let url = URL(string: linkStr) {
            let attributedStringOld = self.allAttributedText
            
            let attributedStringNew = NSMutableAttributedString(string: linkContentStr)
            let linkAttribute: NSAttributedString.Key = self.hyperCustomStyling ? .hyperlink : .link
            let attributesLink: [NSAttributedString.Key: Any] = [
                linkAttribute:  URL(string: linkStr)!,
                .font: R.font.interMedium(size: 14 * Constant.currentSizeScale)!,
                .underlineStyle: NSUnderlineStyle.single.rawValue,
                .underlineColor: UIColor(hexStr: "#005D2D")
            ]
            attributedStringNew.addAttributes(attributesLink, range: NSRange(location: 0, length: attributedStringNew.length))
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = self.hyperTextAlignment
            attributedStringNew.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedStringNew.length))
            
            attributedStringOld.append(attributedStringNew)
            
            self.allUrlAndTitle[url.absoluteString] = linkContentStr;
            self.attributedText = attributedStringOld
        }

    }
    
}
